#ifndef __M_QUE_H__
#define __M_QUE_H__
#include "../mqcommon/mq_logger.hpp"
#include "../mqcommon/mq_helper.hpp"
#include "../mqcommon/mq_msg.pb.h"
#include <iostream>
#include <unordered_map>
#include <mutex>
#include <memory>

namespace castlemq
{
    struct MsgQueue
    {
        using ptr=std::shared_ptr<MsgQueue>;
        std::string name;
        bool durable;
        bool exclusive;//是否独占
        bool auto_delete;
        google::protobuf::Map<std::string, std::string> args;
        
        MsgQueue(){}
        MsgQueue(const std::string& qname,bool qdurable,bool qexclusive,bool qauto_delete,const google::protobuf::Map<std::string, std::string>&& qargs):
        name(qname),durable(qdurable),exclusive(qexclusive),auto_delete(qauto_delete),args(qargs)
        {}

        //args存储键值对，存储数据库时，会组织一个格式字符串进行存储，key=value&key=value...
        void setArgs(const std::string& str_args)//解析str_args存储到args里
        {
            std::vector<std::string> sub_args;
            StrHelper::split(str_args,"&",sub_args);
            for(auto& x:sub_args)
            {
                size_t pos = x.find("=");
                std::string key=x.substr(0,pos);
                std::string val=x.substr(pos+1);
                args[key]=val;
            }
        };

        //序列化args后返回一个字符串
        std::string getArgs()
        {
            std::string ret;
            for(auto it=args.begin();it!=args.end();it++)
            {
                ret+=it->first+"="+it->second+"&";
            }
            return ret;
        }
    };


    class MsgQueueMapper
    {
        public:
            MsgQueueMapper(const std::string& dbfile):_sql_helper(dbfile)
            {
                std::string path = fileHelper::parentDictory(dbfile);
                fileHelper::createDictory(path);
                assert(_sql_helper.open());
                createTable();
            }

            void createTable()
            {
                #define CREAT_QUEUE_TABLE "create table if not exists queue_table(name varchar(32) primary key,\
                durable int,exclusive int,auto_delete int,args varchar(128));"
                char sql_str[4096] = {0};
                sprintf(sql_str,CREAT_QUEUE_TABLE);
                bool ret=_sql_helper.exec(sql_str,nullptr,nullptr);
                if(!ret)
                {
                    DLOG("创建队列表失败！");
                    abort();//结束进程
                }
            }

            void removeTable()
            {
                #define DROP_QUEUE_TABLE "drop table if exists queue_table;"
                char sql_str[4096] = {0};
                sprintf(sql_str,DROP_QUEUE_TABLE);
                bool ret=_sql_helper.exec(sql_str,nullptr,nullptr);
                if(!ret)
                {
                    DLOG("删除队列表失败！");
                }
            }

            bool insert(MsgQueue::ptr& queue)
            {
                #define INSERT_QUEUE_TABLE "insert into queue_table values('%s',%d,%d,%d,'%s');"
                std::string args_str=queue->getArgs();
                char sql_str[4096] = {0};
                sprintf(sql_str,INSERT_QUEUE_TABLE,(queue->name).c_str(),queue->durable,queue->exclusive,queue->auto_delete,args_str.c_str());
                return _sql_helper.exec(sql_str,nullptr,nullptr);
            }

            void remove(const std::string& name)
            {
                #define REMOVE_QUEUE_TABLE "delete from queue_table where name='%s';"
                char sql_str[4096] = {0};
                sprintf(sql_str,REMOVE_QUEUE_TABLE,name.c_str());
                _sql_helper.exec(sql_str,nullptr,nullptr);
            }

            using QueueMap = std::unordered_map<std::string,MsgQueue::ptr>;
            QueueMap recovery()
            {
                QueueMap result;
                std::string ss="select name,durable,exclusive,auto_delete,args from queue_table;";
                _sql_helper.exec(ss.c_str(),SqliteCallback,&result);
                return result;
            }
        private:
            static int SqliteCallback(void* arg,int numcol,char** row,char** fields)
            {
                QueueMap* result = (QueueMap*)arg;
                auto mqp=std::make_shared<MsgQueue>();
                mqp->name=row[0];
                mqp->durable=(castlemq::ExchangeType)std::stoi(row[1]);
                mqp->exclusive=(bool)std::stoi(row[2]);
                mqp->auto_delete=(bool)std::stoi(row[3]);
                if(row[4]) mqp->setArgs(row[4]);
                result->insert(std::make_pair(mqp->name,mqp));
                return 0;
            }
        private:
            SqliteHelper _sql_helper;
    };

    using QueueMap=std::unordered_map<std::string,MsgQueue::ptr>;
    class MsgQueueManager
    {
        public:
        using ptr=std::shared_ptr<MsgQueueManager>;
            MsgQueueManager(const std::string& dbfile):_mapper(dbfile)
            {
                _msg_queues=_mapper.recovery();
            }
            bool declareQueue(const std::string& qname,bool qdurable,bool qexclusive,bool qauto_delete,const google::protobuf::Map<std::string, std::string>& qargs)
            {
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it=_msg_queues.find(qname);
                    if(it!=_msg_queues.end())
                    {
                        return true;//交换机存在就返回
                    }
                    auto mqp=std::make_shared<MsgQueue>();
                    mqp->name=qname;
                    mqp->durable=qdurable;
                    mqp->exclusive=qexclusive;
                    mqp->auto_delete=qauto_delete;
                    mqp->args=qargs;
                    
                    if(qdurable)
                    {
                        if(!_mapper.insert(mqp)) 
                            return false;
                    }
                    _msg_queues.insert(std::make_pair(qname,mqp));
                    return true;
                }
            }
            void deleteQueue(const std::string& name)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it=_msg_queues.find(name);
                if(it==_msg_queues.end())
                    return;//队列不存在就返回
                if(it->second->durable) _mapper.remove(name);
                _msg_queues.erase(name);
            }
            MsgQueue::ptr selectQueue(const std::string& name)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it=_msg_queues.find(name);
                if(it==_msg_queues.end())
                    return MsgQueue::ptr();
                return it->second;
            }
            QueueMap allQueues()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                return _msg_queues;
            }
            bool exists(const std::string& name)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it=_msg_queues.find(name);
                if(it==_msg_queues.end())
                    return false;
                return true;
            }
            size_t size()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                return _msg_queues.size();
            }
            void clear()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _mapper.removeTable();
                _msg_queues.clear();
            }
        private:
            std::mutex _mutex;
            MsgQueueMapper _mapper;
            QueueMap _msg_queues;
    };
}
#endif